package jmine.tec.script.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

import jmine.tec.script.impl.beanshell.BeanshellMessages;
import jmine.tec.script.impl.beanshell.BeanshellScriptExecutorException;
import bsh.EvalError;
import bsh.Interpreter;

/**
 * Esta classe auxilia a instanciar objetos a partir de um script que define uma interface T.
 * 
 * @author adalberto
 * @author eduardo
 */
public class BeanshellUtil {

    /**
     * Este método deve retornar uma instância de T dado um script em beanshell.
     * 
     * @param <T> o tipo da classe
     * @param clazz a interface do objeto T a ser instanciado
     * @param script um byte array que representa o script passado
     * @return T o objeto
     * @throws BeanshellScriptExecutorException excessão que pode ser: Erro ao interpretar o arquivo. O script não respeita a
     * interface/classe.
     */
    @SuppressWarnings("unchecked")
    public <T> T processaScript(final Class<T> clazz, final byte[] script) throws BeanshellScriptExecutorException {
        String strScript = new String(script);
        try {
            return (T) (new Interpreter()).eval(strScript);
        } catch (EvalError e) {
            throw new BeanshellScriptExecutorException(BeanshellMessages.INTERPRETER_ERROR.create(e.getCause(), e, e.getErrorLineNumber(),
                    ""), e);
        }
    }

    /**
     * Este método deve retornar uma instância de T dado um script em beanshell.
     * 
     * @param <T> o tipo da classe
     * @param clazz a interface do objeto T a ser instanciado
     * @param script uma {@link String} que representa o script passado
     * @return T o objeto
     * @throws BeanshellScriptExecutorException excessão que pode ser: Erro ao interpretar o arquivo. O script não respeita a
     * interface/classe.
     */
    public <T> T processaScript(final Class<T> clazz, final String script) throws BeanshellScriptExecutorException {
        return this.processaScript(clazz, script.getBytes());
    }

    /**
     * Este método deve retornar uma instância de T dado um script em beanshell.
     * 
     * @param <T> o tipo da classe
     * @param clazz a interface do objeto T a ser instanciado
     * @param path uma {@link String} que representa o path onde se encontra o script
     * @return T o objeto
     * @throws BeanshellScriptExecutorException excessão que pode ser: Erro ao interpretar o arquivo. O script não respeita a
     * interface/classe. Arquivo não encontrado. Erro ao ler o arquivo
     */
    public <T> T processaArquivoScript(final Class<T> clazz, final String path) throws BeanshellScriptExecutorException {
        Reader input = null;
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(path))));
            input = reader;
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            line = reader.readLine();
            while (line != null) {
                stringBuilder.append(line);
                line = reader.readLine();
            }
            byte[] script = stringBuilder.toString().getBytes();

            return this.processaScript(clazz, script);
        } catch (FileNotFoundException e) {
            throw new IllegalArgumentException(e);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    throw new IllegalArgumentException(e);
                }
            }
        }
    }
}
